package de.mmis.core.infrastructure.gui;

import java.net.InetAddress;
import java.util.LinkedList;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.event.Observable;
import de.mmis.core.base.event.Observer;
import de.mmis.core.base.infrastructure.InfrastructureEvent;
import de.mmis.core.base.infrastructure.PublishedDeviceDescription;
import de.mmis.core.base.infrastructure.PublishedDeviceDescription.PublishType;

/**
 * This class manages all devices (it implements {@link Observer}&lt;
 * {@link InfrastructureEvent}&gt;)
 * 
 * @author Martin Nyolt
 * 
 */
class DeviceTableModel extends AbstractTableModel implements
		Observer<InfrastructureEvent> {

	private static final long serialVersionUID = 4013732541367579462L;

	protected LinkedList<PublishedDeviceDescription> devices = new LinkedList<PublishedDeviceDescription>();
	private List<Object> infoKeys;

	// private ImageIcon activeIcon;
	// private ImageIcon inactiveIcon;

	private static String[] columnNames = new String[] { "Name", "Device Host",
			"Type", "Host", "Port", "Infos" };

	private static Class<?>[] columnClasses = new Class<?>[] { Tree.class,
			String.class, PublishType.class, InetAddress.class, Integer.class,
			Boolean.class };

	static {
		// in order to prevent errors
		if (columnClasses.length != columnNames.length)
			throw new Error(
					"Infrastructure-Monitor: column names and classes don't match");
	}

	public DeviceTableModel(final List<Object> infoKeys) {
		setInfoKeys(infoKeys);
	}

	public void setInfoKeys(List<Object> infoKeys) {
		this.infoKeys = infoKeys;
		if (this.infoKeys == null)
			this.infoKeys = new LinkedList<Object>();

		fireTableStructureChanged();
	}

	@Override
	public int getColumnCount() {
		// Name, device host, publish type, host, port
		return columnNames.length + infoKeys.size();
	}

	/**
	 * @return the appropriate name
	 */
	@Override
	public String getColumnName(int column) {
		if (column < 0 || column >= getColumnCount())
			return "";

		if (column >= columnNames.length)
			return infoKeys.get(column - columnNames.length).toString();

		return columnNames[column];
	}

	/**
	 * @return the appropriate class
	 */
	@Override
	public Class<?> getColumnClass(int column) {
		if (column < 0 || column >= getColumnCount())
			return Object.class;

		if (column >= columnNames.length)
			return Tree.class;

		return columnClasses[column];
	}

	@Override
	public int getRowCount() {
		synchronized (devices) {
			return devices.size();
		}
	}

	public PublishedDeviceDescription getDevice(int row) {
		synchronized (devices) {
			return devices.get(row);
		}
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		PublishedDeviceDescription device;

		synchronized (devices) {
			device = devices.get(rowIndex);
		}

		switch (columnIndex) {
		case 0:
			return device.getID();
		case 1:
			return device.getDeviceHost();
		case 2:
			return device.getType();
		case 3:
			return device.getHost();
		case 4:
			return new Integer(device.getPort());
		case 5:
			return Boolean.valueOf(device.getProperties() != null
					&& !device.getProperties().isEmpty());
		default:
			Object key = infoKeys.get(columnIndex - columnNames.length);
			return device.getProperties().get(key);
		}
	}

	@Override
	public void notify(Observable<? extends InfrastructureEvent> sender,
			final InfrastructureEvent event) {
		// modifications of the list have to be done in the Event Dispatching
		// Thread
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				// Update our device list and notify the JTable
				synchronized (devices) {
					switch (event.getEventType()) {
					case ANSWER_DEVICE:
					case NEW_DEVICE:
						if (devices.contains(event.getDevice()))
							return;
						devices.addLast(event.getDevice());
						fireTableRowsInserted(devices.size() - 1,
								devices.size() - 1);
						break;
					case DEVICE_REMOVED:
						devices.remove(event.getDevice());
						fireTableDataChanged();
						break;
					case SHUT_DOWN:
						int lastRow = devices.size() - 1;
						devices.clear();
						fireTableRowsDeleted(0, lastRow);
						break;
					case MIGRATED:
					case NEW_MACHINE:
					case SHUT_DOWN_ANNOUNCEMENT:
					case HEARTBEAT:
						// ignore
						break;
					}
				}
			}
		});
	}

	public void clear() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				synchronized (devices) {
					if (devices.size() <= 0)
						return;
					int lastRow = devices.size() - 1;
					devices.clear();
					fireTableRowsDeleted(0, lastRow);
				}
			}
		});
	}

}
